#include "mmp_lib.h"
#include "config_fw.h"

#if SUPPORT_UAC
#if (AUDEXT_DAC == WM8973)
#include "lib_retina.h"
#include "mmp_reg_gbl.h"
#include "mmp_reg_vif.h"
#include "mmpd_system.h"
#include "mmps_audio.h"
#include "wm8973.h"
#include "mmpf_i2cm.h"
#include "lib_retina.h"
#include "mmpf_usbuac.h"

//using BGPIO16 17
static MMPF_I2CM_ATTRIBUTE m_WM8973_I2cmAttribute = {MMPF_I2CM_ID_0, WM8973_I2C_DEV_ADDR, 8, 8, 0, MMP_FALSE, MMP_FALSE, MMP_FALSE, MMP_FALSE, 0, 0, 0, MMPF_I2CM_SPEED_HW_250K, NULL, NULL};
static MMP_BOOL m_bCodecInitialized = MMP_FALSE;
static MMP_BOOL m_bCodecAsMaster = MMP_TRUE;
static AUDIO_EXTCODEC_PATH m_ulCodecPath = AUDIO_EXT_PATH_MAX;


MMP_BOOL gbCodecPowerUp = MMP_FALSE;
extern MMP_SHORT  gsVol;

//*----------------------------------------------------------------------------
//* Function Name       : SetI2CDelay
//* Input Parameters    : none
//* Output Parameters   : none
//* Functions called    : none
//*----------------------------------------------------------------------------
void SetI2CDelay(void)
{
    int   j;

    for(j=0; j < I2C_DELAY ;j++);
}

//*----------------------------------------------------------------------------
// function :MMPF_WM8973_WriteReg
// input    : (Register address, Register data), reg 7 bits, data 9 bits
// output   : none
// descript : write codec register
//*----------------------------------------------------------------------------
void WM8973_WriteReg(MMPF_I2CM_ATTRIBUTE* uI2cmAttribute, MMP_USHORT usReg, MMP_USHORT usData)
{
    usReg = (usReg << 1) | (usData >> 8);//reg 7 bits, data 9 bits
    usData &= (0xFF);
    MMPF_I2cm_WriteReg(uI2cmAttribute, usReg, usData);
    return;
}

//*----------------------------------------------------------------------------
// function :MMPF_WM8973_WriteReg
// input    : (Register address, Register data), reg 7 bits, data 9 bits
// output   : none
// descript : write codec register
//*----------------------------------------------------------------------------
void WM8973_ReadReg(MMPF_I2CM_ATTRIBUTE* uI2cmAttribute, MMP_USHORT usReg, MMP_USHORT* usData)
{
    usReg = (usReg << 1);//reg 7 bits, data 9 bits
    //usData &= (0xFF);
    MMPF_I2cm_ReadReg(uI2cmAttribute, usReg, usData);
    
    return;
}
//*----------------------------------------------------------------------------
// function : InitExtDac
// input    : none
// output   : none
// descript : Initial external dac
//*----------------------------------------------------------------------------
void InitExtDac(void)
{
    RTNA_DBG_Str0("InitExtDac \r\n");
    
    MMPF_I2cm_Initialize(&m_WM8973_I2cmAttribute);

    /*Headphone*/
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_RESET,0x000); //reset
    if (m_bCodecAsMaster == MMP_FALSE) { // slave
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_AUDIO_INTERFACE,RIGHT_JUSTIFIED_FORMAT | AUDIO_DATA_LENGTH_16);//16 bit right-justified
    }
    else {
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_AUDIO_INTERFACE, I2S_FORMAT | ENABLE_MASTER_MODE | AUDIO_DATA_LENGTH_16);// master mode, I2S format, 20 bits word size
    }
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_OUT_MIX1,0x100);//left DAC to left mixer
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_OUT_MIX2,0x100);//right DAC to right mixer
}
//*----------------------------------------------------------------------------
// function : PowerUpExtDAC
// input    : none
// output   : none
// descript : Poweron external dac
//*----------------------------------------------------------------------------
void PowerUpExtDAC(MMP_USHORT sampling_rate)
{
    RTNA_DBG_Str0("PowerUpExtDAC \r\n");

    /*Headphone*/
    if (m_bCodecAsMaster == MMP_FALSE) { // slave
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT1, 0x1C0);//enable VREF
    }
    else {
        WM8973_WriteReg(&m_WM8973_I2cmAttribute,  WM8973_PWR_MGMT1, 0x1FC);//enable VREF, power up ADC for master mode
    }
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT2,0x1E0);//power up LOUT1, ROUT2
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_ADC_DAC_CONTROL,0x000);//diable mute
}
//*----------------------------------------------------------------------------
// function : PowerDownExtDAC
// input    : none
// output   : none
// descript : Power down external dac
//*----------------------------------------------------------------------------
void PowerDownExtDAC(void)
{
	if (gbCodecPowerUp == MMP_TRUE)
	{
	    RTNA_DBG_Str0("PowerDownExtDAC \r\n");

	    /*Headphone*/
	    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_ADC_DAC_CONTROL,0x008);//Enable mute
	    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT2,0x0);//power down LOUT1,ROUT1
	    gbCodecPowerUp = MMP_FALSE;
    }
    else return;
}

//*----------------------------------------------------------------------------
// function : InitExtMicIn
// input    : none
// output   : none
// descript : Initial external mic in
//*----------------------------------------------------------------------------
void InitExtMicIn(void)
{
    MMPF_I2cm_Initialize(&m_WM8973_I2cmAttribute);

    RTNA_DBG_Str0("InitExtMicIn \r\n");

    /*Mic*/
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_RESET,0x000); //reset
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_AUDIO_INTERFACE, LEFT_JUSTIFIED_FORMAT | AUDIO_DATA_LENGTH_16);//16 bit left-justified
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_ADCR_SIGNAL_PATH,0x60);
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_INPUT_VOLUME,0x117);
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_INPUT_VOLUME,0x117);

    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_ADC_VOLUME,0x1C3);
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_ADC_VOLUME,0x1C3);
}
//*----------------------------------------------------------------------------
// function : PowerUpExtMicIn
// input    : none
// output   : none
// descript : Poweron external mic in
//*----------------------------------------------------------------------------
void PowerUpExtMicIn(void)
{
    RTNA_DBG_Str0("PowerUpExtMicIn \r\n");

    /*Mic*/
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT1,0x0FE);
}
//*----------------------------------------------------------------------------
// function : PowerDownExtMicIn
// input    : none
// output   : none
// descript : Power down external mic in
//*----------------------------------------------------------------------------
void PowerDownExtMicIn(void)
{
    RTNA_DBG_Str0("PowerDownExtMicIn \r\n");

    /*Mic*/
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT1,0x000);
}

MMP_USHORT gsVolume = 0x39;
//*----------------------------------------------------------------------------
// function : InitExtLineIn
// input    : none
// output   : none
// descript : Initial external line in
//*----------------------------------------------------------------------------
void InitExtLineIn(void)
{
    AITPS_GBL   pGBL = AITC_BASE_GBL;
    
    //RTNA_DBG_Str3("InitExtLineIn \r\n");
    dbg_printf(3, "InitExtLineIn \r\n");

	pGBL->GBL_CLK_DIS0 &= ~(GBL_CLK_VI_DIS);
	pGBL->GBL_CLK_DIS1 &= ~(GBL_CLK_I2C_DIS);
    
    MMPF_I2cm_Initialize(&m_WM8973_I2cmAttribute);

    /*Line in*/
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_RESET,0x000); //reset
    if (m_bCodecAsMaster == MMP_FALSE) { // slave
    	RTNA_DBG_Str(0, "slave\r\n");
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_AUDIO_INTERFACE, LEFT_JUSTIFIED_FORMAT | AUDIO_DATA_LENGTH_16);//16 bit left-justified
    }
    else {
    	RTNA_DBG_Str(0, "master\r\n");
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_AUDIO_INTERFACE, LEFT_JUSTIFIED_FORMAT | AUDIO_DATA_LENGTH_16 | ENABLE_MASTER_MODE);
        //WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_SAMPLE_RATE, 0x100);
    }
    
#if 0    
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_INPUT_VOLUME, (gsVolume | 0x140));    
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_INPUT_VOLUME, (gsVolume | 0x140));
#else
    MMPC_AudioExtCodec_SetRecordVolume(gsVol);
#endif    
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_ADC_VOLUME, 0x1D3);
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_ADC_VOLUME, 0x1D3);
    
}
//*----------------------------------------------------------------------------
// function : PowerUpExtLinIn
// input    : none
// output   : none
// descript : Poweron external line in
//*----------------------------------------------------------------------------
void PowerUpExtLinIn(MMP_ULONG samplerate)
{
    //RTNA_DBG_Str0("PowerUpExtLinIn \r\n");
    dbg_printf(3, "PowerUpExtLinIn \r\n");

    /*Line in*/
    if (m_bCodecAsMaster == MMP_FALSE) { // slave
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT1,0x0FE);
    }
    else {
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT1, 0x1FE );

        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT2,0x1E0);//power up LOUT1, ROUT2
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_ADC_DAC_CONTROL,0x000);//diable mute
    }
    gbCodecPowerUp = MMP_TRUE;
}
//*----------------------------------------------------------------------------
// function : PowerDownExtLineIn
// input    : none
// output   : none
// descript : Power down external line in
//*----------------------------------------------------------------------------
void PowerDownExtLineIn(void)
{
    //RTNA_DBG_Str0("PowerDownExtLineIn \r\n");
    dbg_printf(3, "PowerDownExtLineIn\r\n");

    /*Line in*/
    if (gbCodecPowerUp == MMP_TRUE){
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT1,0x000);
    	gbCodecPowerUp = MMP_FALSE;
    }
}
//*----------------------------------------------------------------------------
// function : InitExtLineInDac
// input    : none
// output   : none
// descript : Initial external dac and line in
//*----------------------------------------------------------------------------
void InitExtLineInDac(void)
{
    MMPF_I2cm_Initialize(&m_WM8973_I2cmAttribute);

    RTNA_DBG_Str0("InitExtLineInDac \r\n");

    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_RESET,0x000); //reset
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_AUDIO_INTERFACE,LEFT_JUSTIFIED_FORMAT | AUDIO_DATA_LENGTH_16);
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_INPUT_VOLUME,0x128);
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_INPUT_VOLUME,0x128);

    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_ADC_VOLUME,0x1C3);
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_ADC_VOLUME,0x1C3);

    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_OUT_MIX1,0x100);//left DAC to left mixer
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_OUT_MIX2,0x100);//right DAC to right mixer
}
//*----------------------------------------------------------------------------
// function : PowerupExtLineInDac
// input    : none
// output   : none
// descript : Power up external dac and line in
//*----------------------------------------------------------------------------
void PowerUpExtLineInDac(void)
{
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT1, 0x1FE); 
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_PWR_MGMT2, 0x1E0);
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_ADC_DAC_CONTROL, 0x000); //diable mute
}

/** @brief Inhouse Wolfson audio CODEC initializer.

See @ref AudioInitializer for more information.
*/
MMP_BOOL AudioInitializer(MMP_USHORT cmd)
{
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_TRUE);
    if (cmd & AUDIO_INIT_OUT) {
    	InitExtDac();
    	PowerUpExtLinIn(32000);
    }

    if (cmd & AUDIO_INIT_LINEIN) {
        InitExtLineIn();
        PowerUpExtLinIn(32000);
    }
    if (cmd & AUDIO_INIT_MICIN) {
        InitExtMicIn();
        PowerUpExtMicIn();
    }
    if (cmd & AUDIO_INIT_OUT_LINEIN) {
        InitExtLineInDac();
        PowerUpExtLineInDac();
    }
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_FALSE);

    return MMP_TRUE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetSampleRate
// input    : ulSamplerate: external codec runs in USB mode needs to change PLL 
//            according to sample rate
// output   : none
// descript : Config external codec PLL to specified sample rate,
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetSampleRate(MMP_ULONG ulSamplerate)
{
    //RTNA_DBG_PrintLong(0,ulSamplerate);
    dbg_printf(3, "ulSamplerate = %d\r\n", ulSamplerate);
    
    if(ulSamplerate == 8000)
	  WM8973_WriteReg(&m_WM8973_I2cmAttribute,WM8973_SAMPLE_RATE,0x06 << 1 | 0x01);
	else if(ulSamplerate == 16000)
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_SAMPLE_RATE, 0x0A << 1 | 0x01);
    else if(ulSamplerate == 24000)  
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_SAMPLE_RATE, 0x1C << 1 | 0x01);
	else if(ulSamplerate == 32000)
	  WM8973_WriteReg(&m_WM8973_I2cmAttribute,WM8973_SAMPLE_RATE,0x0C << 1 | 0x01);
 	else// 48K
	  WM8973_WriteReg(&m_WM8973_I2cmAttribute,WM8973_SAMPLE_RATE,0x00 | 0x01);
	
	//WM8973_WriteReg(&m_WM8973_I2cmAttribute,0x0D, 0xFF);
    return MMP_ERR_NONE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetMute
// input    : bMute: set MMP_TRUE to mute, MMP_FALSE to un-mute 
// output   : none
// descript : Config external codec mute/un-mute.
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetMute(MMP_BOOL bMute)
{
    if (bMute) {
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_INPUT_VOLUME, 0x180);
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_INPUT_VOLUME, 0x180);
    }
    else {
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_INPUT_VOLUME, (gsVolume | 0x100)); // bit 6 must be set to zero while un-mute
        WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_INPUT_VOLUME, (gsVolume | 0x100)); // bit 6 must be set to zero while un-mute
    }
    dbg_printf(0, "bMute = %d, gsVolume = 0x%x\r\n", bMute, gsVolume);

    return MMP_ERR_NONE;
}
//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetVolume
// input    : level: external codec volume level
// output   : none
// descript : Config external codec volume level,
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetVolume(MMP_ULONG level)
{
    return MMP_ERR_NONE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetVolume
// input    : level: external codec volume level
// output   : none
// descript : Config external codec volume level,
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetRecordVolume(MMP_LONG dB)//unit is 0.01dB, 0.75dB for each step, input range is +3000~-1725
{
    
    if(dB > VOL_MAX)
        dB = VOL_MAX;
    if(dB < -1725)
        dB = -1725;
    
    gsVolume = (MMP_USHORT)(dB / 75 + 0x17);

    dbg_printf(3, "gsVolume:0x%x\r\n", gsVolume);

    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_L_INPUT_VOLUME, (gsVolume | 0x140));
    WM8973_WriteReg(&m_WM8973_I2cmAttribute, WM8973_R_INPUT_VOLUME, (gsVolume | 0x140));
    
    return MMP_ERR_NONE;
}
//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetPath
// input    : path: please refer MMPF_AUDIO_EXTCODEC_PATH for path can be set
// output   : none
// descript : Config external codec path if alreay iniialized, otherwise,
//            keep the path settings only.
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetPath(MMP_ULONG path)
{
    if (m_bCodecInitialized == MMP_TRUE) {
        //reset codec path
        switch (m_ulCodecPath) {
        case AUDIO_EXT_HP_OUT:
            AudioInitializer(AUDIO_INIT_OUT);
            break;
        case AUDIO_EXT_MIC_IN:
            AudioInitializer(AUDIO_INIT_MICIN);
            break;
        case AUDIO_EXT_AUX_IN:
            AudioInitializer(AUDIO_INIT_LINEIN);
            break;
        case AUDIO_EXT_AUX_BYPASS_HP:
            AudioInitializer(AUDIO_INIT_OUT_LINEIN);
            break;
        default:
            return MMP_AUDIO_ERR_PARAMETER;
            break;
        }
    }
    m_ulCodecPath = path;

    return MMP_ERR_NONE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_Init
// input    : samplerate: Sample rate
//            bSetAsMaster: Set codec as master mode or slave mode
// output   : none
// descript : Initialize and power up codec according to path settings,
//            be careful, please call MMPC_AudioExtCodec_SetPath first to set path
//*----------------------------------------------------------------------------
/*
MMP_ERR MMPC_AudioExtCodec_Init(MMP_UBYTE I2SMode, MMP_ULONG samplerate, MMP_ULONG ulFixedMclkFreq)
{
    if (m_ulCodecPath == AUDIO_EXT_PATH_MAX) {
        RTNA_DBG_Str0("Please set path first before init codec!\r\n");
        return MMP_AUDIO_ERR_INVALID_FLOW;
    }
    //0 -> master , 1 ->slave
    if (I2SMode == 0) {
        //set AIT as master, set codec as slave
        m_bCodecAsMaster = MMP_FALSE;
    }
    else {
        //set AIT as slave, set codec as master
        m_bCodecAsMaster = MMP_TRUE;
    }

    switch (m_ulCodecPath) {
    case AUDIO_EXT_HP_OUT:
        AudioInitializer(AUDIO_INIT_OUT);
        break;
    case AUDIO_EXT_MIC_IN:
        AudioInitializer(AUDIO_INIT_MICIN);
        break;
    case AUDIO_EXT_AUX_IN:
        AudioInitializer(AUDIO_INIT_LINEIN);
        break;
    case AUDIO_EXT_AUX_BYPASS_HP:
        AudioInitializer(AUDIO_INIT_OUT_LINEIN);
        break;
    default:
        return MMP_AUDIO_ERR_PARAMETER;
        break;
    }
    m_bCodecInitialized = MMP_TRUE;

    return MMP_ERR_NONE;
}
*/
//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_Uninit
// input    : none
// output   : none
// descript : Uninitialize and power down codec according to path settings.
//*----------------------------------------------------------------------------
/*
MMP_ERR MMPC_AudioExtCodec_Uninit(void)
{
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_TRUE);

    if (m_bCodecInitialized) {
        switch (m_ulCodecPath) {
        case AUDIO_EXT_HP_OUT:
            PowerDownExtDAC();
            break;
        case AUDIO_EXT_MIC_IN:
            PowerDownExtMicIn();
            break;
        case AUDIO_EXT_AUX_IN:
            PowerDownExtLineIn();
            break;
        case AUDIO_EXT_AUX_BYPASS_HP:
            PowerDownExtDAC();
            PowerDownExtLineIn();
            break;
        default:
            return MMP_AUDIO_ERR_PARAMETER;
            break;
        }
        m_bCodecInitialized = MMP_FALSE;
        //m_ulCodecPath = MMPF_AUDIO_EXT_PATH_MAX;
    }
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_FALSE);

    return MMP_ERR_NONE;
}
*/
#endif
#endif